import vtk
import wx

from wx.lib.pubsub import Publisher
from HeaDSETGeom import *

class HeaDSETDicom(HeaDSETGeom):
	'''
		Loads a dicom directory and modifies it to make a reasonable mesh of it
		for usage in HeaDSET.
		
		The code is inspired from the opensource software Osirix. The defaults
		are chosen too closely match theirs. 
		
		Due to the length of the operations involved in creating the geom a 
		progress bar is show during the geom creation. 
	'''
	
	# The following values are taken from OSIRIX.
	BONE_EXTRACTOR = 300
	DEFAULT_REDUCTION = 0.95
	SMOOTHER_ITERATIONS = 20
	
	# Don't touch these
	SMOOTHER_RELAXATION = 0.05
	NORMAL_FEATURE_ANGLE = 120
	DEFAULT_COLOR = (0.9, 0.9, 0.9)
	DEFAULT_OPACITY = 0.5
	
	#constructor
	def __init__(self, directory, label = HeaDSETGeom.DICOM, extractorValue = BONE_EXTRACTOR, dicomReduction = DEFAULT_REDUCTION, smoothing = SMOOTHER_ITERATIONS, color = DEFAULT_COLOR, opacity = DEFAULT_OPACITY):
		'''
			Creates the geom from the information provided in the directory. 
			The algoritm first loads the files. Then it filters the contours
			of the volume. This contour is triangulated and decimate to generate 
			a mesh. This mesh is smoothed to get a nicer looking result. The 
			generated mesh is then put in our vtk actor. 
			
			Most arguments have sensible defaults. 
			
			@param directory: The location of the dicom directory.s
			@param label: The desired name for the dicom
			@param extractorValue: The value that is extract sensible data from the dicom images. (skin:-500, bone:300, metal:500)
			@param dicomReduction: The amount the reduction. Should remove nearly everything at 1 and should do nothing at 0. 
			@param smoothing: Number of smoothing passes. 
			@param color: The desired color for this geom.
			@param opacity: The desired opacity for this geom.
		'''
		self.__failed = False
		self.__patientName = "Nobody"
		self.__directory = directory
		progress = wx. ProgressDialog(HeaDSETGeom.DICOM, "Loading file")
		progress.Update(0, "Loading file")
		reader = vtk.vtkDICOMImageReader()
		reader.AddObserver("ErrorEvent", self._OnFail)
		
		print "Set directory"
		reader.SetDirectoryName(directory)
		reader.Update()
		
		self.__duringCallBack = True
		if not self.__failed:
			print "Contour filter"
			progress.Update(10, "Contour Filter")
			boneExtractor = vtk.vtkContourFilter()
			print "NumContours: " + str(boneExtractor.GetNumberOfContours())
			print "ComputeNormals: " + str(boneExtractor.GetComputeNormals())
			print "ComputeGradients: " + str(boneExtractor.GetComputeGradients())
			print "ComputeScalars: " + str(boneExtractor.GetComputeScalars())
			print "ScalarTree: " + str(boneExtractor.GetUseScalarTree())
			#boneExtractor.SetNumberOfContours(2) # change resolution?
			#boneExtractor.SetComputeNormals()
			boneExtractor.SetInputConnection(reader.GetOutputPort())
			boneExtractor.SetValue(0, extractorValue)
			
			print "Triangulate"
			progress.Update(20, "Triangle Filter")
			triangleFilter = vtk.vtkTriangleFilter()
			triangleFilter.SetInputConnection(boneExtractor.GetOutputPort())
			print "PassVertsOn: " + str(triangleFilter.GetPassVerts())
			print "PassLinesOn: " + str(triangleFilter.GetPassLines())
			triangleFilter.Update()
			
			print "Decimator"
			progress.Update(25, "Decimate Filter")
			boneDecimator = vtk.vtkDecimatePro()
			#boneDecimator.SetInputConnection(boneExtractor.GetOutputPort())
			boneDecimator.SetInputConnection(triangleFilter.GetOutputPort())
			boneDecimator.SetTargetReduction(dicomReduction)
			boneDecimator.SetPreserveTopology(True)
			print "Splitting: " + str(boneDecimator.GetSplitting())
			print "SplitAngle: " + str(boneDecimator.GetSplitAngle())
			print "FeatureAngle: " + str(boneDecimator.GetFeatureAngle())
			print "PresplitMesh: " + str(boneDecimator.GetPreSplitMesh())
			print "VertexBoundaryDeletion: " + str(boneDecimator.GetBoundaryVertexDeletion())
			print "Degree: " + str(boneDecimator.GetDegree())
			print "InflectionPointRatio: " + str(boneDecimator.GetInflectionPointRatio())
			boneDecimator.Update()
			
			print "Smoother"
			progress.Update(60, "Smoother Filter")
			boneSmoother = vtk.vtkSmoothPolyDataFilter()
			boneSmoother.SetInputConnection(boneDecimator.GetOutputPort())
			boneSmoother.SetNumberOfIterations(smoothing)
			boneSmoother.SetRelaxationFactor(HeaDSETDicom.SMOOTHER_RELAXATION)
			boneSmoother.Update()
			
			print "Normals"
			progress.Update(80, "Misc Filters")
			boneNormals = vtk.vtkPolyDataNormals()
			boneNormals.SetInputConnection(boneSmoother.GetOutputPort())
			boneNormals.SetFeatureAngle(HeaDSETDicom.NORMAL_FEATURE_ANGLE)
			
			print "PolyData mapper"
			boneMapper = vtk.vtkPolyDataMapper()
			boneMapper.SetInputConnection(boneNormals.GetOutputPort())
			boneMapper.ScalarVisibilityOff()
			
			print "Breathing live to actor"
			bone = HeaDSETVTKActor()
			prop = bone.GetProperty()
			prop.BackfaceCullingOn()
			bone.SetProperty(prop)
			bone.SetMapper(boneMapper)	
			bone.SetGeom(self)
			progress.Update(90, "Actor operations")
			
			xr, yr, zr = bone.GetCenter()
			#bone.SetOrigin(xr, yr, zr)
			
			# call super constructor
			HeaDSETGeom.__init__(self, bone, label)
			
			self.__patientName = reader.GetPatientName()
			print reader.GetPatientName()

			r,g,b = color
			self.SetColor(r,g,b)
			self.SetOpacity(opacity)
			self.SetPosition(-xr, -yr, -zr) # position dicom in center of world
			
			print "Done"
		else:
			bone = HeaDSETVTKActor()
			HeaDSETGeom.__init__(self, bone, label)
				
		progress.Destroy()
	
	def _OnFail(self,  widget, eventString):
		self.__failed  = True
	
	def IsFailed(self):
		return self.__failed
	
	def TypeClone(self):
		'''
			Since cloning a dicom geom makes no sense this method returns None.
			
			@return: None
		'''
		return None

	def GetDirectory(self):
		'''
			Returns the directory that was used to create the dicom geom.
		
			@return: The directory of the original dicom data.
		'''
		return self.__directory

	
	def GetPatientName(self):
		'''
			Returns the name of the patient that was used to create ths geom.
			
			@return: The patient name.
		'''
		return self.__patientName
